package com.evil.application.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evil.application.enums.order.OrderSpecialControlEnum;
import com.evil.application.mapper.*;
import com.evil.application.pojo.bo.application.robot.rule.ApplicationRobotRuleBO;
import com.evil.application.pojo.dto.form.control.ControlBaseInfoDTO;
import com.evil.application.pojo.dto.form.control.code.ControlDTO;
import com.evil.application.pojo.entity.*;
import com.evil.application.service.ApplicationFormControlService;
import com.evil.application.service.ApplicationRobotRuleService;
import com.evil.application.util.ApplicationUtil;
import com.evil.application.util.MatchingUtil;
import com.evil.common.activiti.enums.NodeTypeEnum;
import com.evil.common.application.dto.SourceDTO;
import com.evil.common.application.dto.form.SpecialControlConfigDTO;
import com.evil.common.application.dto.order.data.ControlDataValueDTO;
import com.evil.common.application.dto.robot.RobotConfigDTO;
import com.evil.common.application.dto.robot.RobotRuleDTO;
import com.evil.common.application.dto.robot.rule.CopyRobotRuleReqDTO;
import com.evil.common.application.dto.robot.rule.RobotRuleSourceDTO;
import com.evil.common.application.dto.robot.rule.RobotRulesDTO;
import com.evil.common.application.enums.ApplicationVersionEnum;
import com.evil.common.application.enums.button.ButtonSourceTypeEnum;
import com.evil.common.application.enums.robot.RobotRuleSourceTypeEnum;
import com.evil.common.application.enums.robot.RobotSourceTypeEnum;
import com.evil.common.core.enums.BaseEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.StreamUtil;
import com.evil.common.family.enums.RCodeEnum;
import com.evil.common.core.enums.SwitchEnum;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 机器人规则表
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
@RestController
@AllArgsConstructor
public class ApplicationRobotRuleServiceImpl extends ServiceImpl<ApplicationRobotRuleMapper, ApplicationRobotRule> implements ApplicationRobotRuleService {

    private final ApplicationFormMapper applicationFormMapper;

    private final ApplicationProcessMapper applicationProcessMapper;

    private final ApplicationProcessNodeMapper applicationProcessNodeMapper;

    private final ApplicationButtonMapper applicationButtonMapper;

    private final ApplicationViewMapper applicationViewMapper;

    private final ApplicationRobotMapper applicationRobotMapper;

    private final ApplicationFormControlService applicationFormControlService;

    private final ApplicationLogServiceImpl logService;

    /**
     * 获取机器人规则
     *
     * @param sourceDTO sourceDTO
     * @return List
     */
    @Override
    public List<RobotRuleDTO> findBySource(RobotRuleSourceDTO sourceDTO) {
        return this.toDTO(sourceDTO, this.baseMapper.findBySource(sourceDTO));
    }

    /**
     * 获取机器人规则
     *
     * @param sourceTypeEnum sourceTypeEnum
     * @param sourceIds      sourceIds
     * @return Map
     */
    @Override
    public Map<Long, List<RobotRuleDTO>> findMapBySourceIds(RobotRuleSourceTypeEnum sourceTypeEnum, Collection<Long> sourceIds) {
        List<RobotRuleSourceDTO> sources = sourceIds.stream().map(e -> new RobotRuleSourceDTO(sourceTypeEnum, e)).collect(Collectors.toList());
        Map<RobotRuleSourceDTO, List<ApplicationRobotRule>> sourceRulesMap = this.baseMapper.findBySources(sources).stream()
                .collect(Collectors.groupingBy(e -> new RobotRuleSourceDTO(e.getSourceType(), e.getSourceId())));
        return StreamUtil.transMapKV(sourceRulesMap, SourceDTO::getSourceId, this::toDTO);
    }

    /**
     * 获取机器人规则
     *
     * @param sourceDTO sourceDTO
     * @return List
     */
    @Override
    public List<ApplicationRobotRuleBO> findBOBySource(RobotRuleSourceDTO sourceDTO) {
        return ApplicationUtil.classifyRobotRule(this.baseMapper.findBySource(sourceDTO));
    }

    /**
     * 保存机器人规则
     *
     * @param robotRulesDTO robotRuleSourceDTO
     */
    @Override
    public void saveRobotRule(RobotRulesDTO robotRulesDTO) {
        Long enterpriseId = robotRulesDTO.getEnterpriseId();
        List<RobotRuleDTO> robotRules = robotRulesDTO.getRobotRules();
        Long targetApplicationId = robotRulesDTO.getTargetApplicationId();

        List<ControlDTO> controls = applicationFormControlService.appReleaseControlDTOs(targetApplicationId, true);
        controls = ApplicationUtil.classifyControl(controls);
        Map<Long, ControlDTO> controlMap = StreamUtil.toMapK(controls, ControlDTO::getControlId);

        // 校验同一种规则 目标id不能重复
        Set<String> sourceIds = new HashSet<>();
        robotRules.forEach(rule -> {
            if (!sourceIds.add(String.format("%s_%s", rule.getRobotRuleType(), rule.getRuleTargetId()))) {
                throw new BusinessException(RCodeEnum.ROBOT_RULE_TARGET_CANT_REPEAT);
            }
            MatchingUtil.check(rule.getMatchingType(), rule.getValues(), ControlDataValueDTO::getValue, "规则动态类型错误");
        });

        List<ApplicationRobotRule> robotRuleList = this.baseMapper.findBySource(robotRulesDTO);
        Map<Long, ApplicationRobotRule> robotRuleMap = StreamUtil.toMapK(robotRuleList, ApplicationRobotRule::getRobotRuleId);

        List<Long> ids = new ArrayList<>();
        BiFunction<RobotRuleDTO, Long, ApplicationRobotRule> save = (robotRule, parentRuleId) -> {
            ApplicationRobotRule applicationRobotRule;
            if (null != robotRule.getRobotRuleId()) {
                // 编辑
                if (!robotRuleMap.containsKey(robotRule.getRobotRuleId())) {
                    throw new BusinessException(BaseEnum.ERROR_PARAMETER, "规则信息错误");
                }
                ids.add(robotRule.getRobotRuleId());
                applicationRobotRule = robotRuleMap.get(robotRule.getRobotRuleId());
            } else {
                // 新增
                applicationRobotRule = new ApplicationRobotRule();
                applicationRobotRule.setEnterpriseId(enterpriseId);
                applicationRobotRule.setSourceRobotRuleId(ApplicationUtil.DEFAULT_SOURCE_ROBOT_RULE_ID);
            }
            BeanUtil.copyProperties(robotRule, applicationRobotRule);
            applicationRobotRule.setDefaultValue(JSON.toJSONString(robotRule.getValues()));
            applicationRobotRule.setSourceType(robotRulesDTO.getSourceType());
            applicationRobotRule.setSourceId(robotRulesDTO.getSourceId());
            applicationRobotRule.setParentRobotRuleId(parentRuleId);

            logService.saveAndCheck(this.baseMapper, ApplicationRobotRule::getRobotRuleId, applicationRobotRule);
            return applicationRobotRule;
        };

        // 检查 规则目标id
        BiFunction<RobotRuleDTO, Map<Long, ControlDTO>, ControlDTO> getTargetControl = (robotRule, targetMap) -> {
            if (!targetMap.containsKey(robotRule.getRuleTargetId())) {
                throw new BusinessException(RCodeEnum.APPLICATION_FORM_CONTROL_NOT_EXIST);
            }
            return targetMap.get(robotRule.getRuleTargetId());
        };

        // 遍历，固定只有两层级
        robotRules.forEach(robotRule -> {
            // 规则目标id
            ControlDTO control = getTargetControl.apply(robotRule, controlMap);
            // 目标子级
            Map<Long, ControlDTO> childTargetMap = StreamUtil.toMapK(control.getChildControls(), ControlDTO::getControlId);

            ApplicationRobotRule applicationRobotRule = save.apply(robotRule, ApplicationUtil.DEFAULT_PARENT_ROBOT_RULE_ID);
            // 保存子级规则
            robotRule.getChildren().forEach(child -> {
                // 子级规则目标id
                getTargetControl.apply(child, childTargetMap);
                save.apply(child, applicationRobotRule.getRobotRuleId());
            });
        });

        // 删除
        List<ApplicationRobotRule> deletedList = robotRuleList.stream()
                .filter(e -> !ids.contains(e.getRobotRuleId()))
                .peek(e -> e.setIsDeleted(SwitchEnum.YES.getId()))
                .collect(Collectors.toList());
        logService.saveAndCheck(this.baseMapper, ApplicationRobotRule::getRobotRuleId, deletedList);
    }

    /**
     * 复制机器人规则
     *
     * @param copyRobotRuleReqDTO copyRobotRuleDTO
     */
    @Override
    public void copyRobotRule(CopyRobotRuleReqDTO copyRobotRuleReqDTO) {
        Long enterpriseId = copyRobotRuleReqDTO.getEnterpriseId();
        RobotRuleSourceDTO source = copyRobotRuleReqDTO.getSource();
        RobotRuleSourceDTO target = copyRobotRuleReqDTO.getTarget();
        Map<Long, Long> controlIdMap = copyRobotRuleReqDTO.getControlIdMap();
        boolean updateSource = copyRobotRuleReqDTO.getUpdateSource();

        List<ApplicationRobotRuleBO> sourceRobotRules = this.findBOBySource(source);
        List<ApplicationRobotRule> targetRobotRules = this.baseMapper.findBySource(target);

        Predicate<? super ApplicationRobotRule> filter = e -> !ApplicationUtil.DEFAULT_SOURCE_ROBOT_RULE_ID.equals(e.getSourceRobotRuleId());
        Map<Long, ApplicationRobotRule> targetRobotRuleSourceMap = StreamUtil.toMapFK(targetRobotRules, filter, ApplicationRobotRule::getSourceRobotRuleId);

        List<Long> ids = new ArrayList<>();
        BiFunction<ApplicationRobotRuleBO, Long, ApplicationRobotRule> copy = (robotRule, parentRuleId) -> {
            // 编辑/新增
            ApplicationRobotRule copyRule = this.copyProp(robotRule, targetRobotRuleSourceMap.getOrDefault(robotRule.getRobotRuleId(), null));
            copyRule.setEnterpriseId(enterpriseId);
            copyRule.setSourceType(target.getSourceType());
            copyRule.setSourceId(target.getSourceId());
            copyRule.setParentRobotRuleId(parentRuleId);

            // 需要替换控件id && 当前不是默认控件id
            if (Objects.nonNull(controlIdMap) && !controlIdMap.containsValue(copyRule.getRuleSourceId()) && !ApplicationUtil.DEFAULT_CONTROL_ID.equals(copyRule.getRuleSourceId())) {
                if (!controlIdMap.containsKey(copyRule.getRuleSourceId())) {
                    throw new BusinessException(RCodeEnum.APPLICATION_FORM_CONTROL_NOT_EXIST);
                }
                copyRule.setRuleSourceId(controlIdMap.get(copyRule.getRuleSourceId()));
            }

            ids.add(copyRule.getRobotRuleId());
            logService.saveAndCheck(this.baseMapper, ApplicationRobotRule::getRobotRuleId, copyRule);

            if (updateSource && !robotRule.getSourceRobotRuleId().equals(copyRule.getRobotRuleId())) {
                // 更新源信息
                robotRule.setSourceRobotRuleId(copyRule.getRobotRuleId());
                logService.saveAndCheck(this.baseMapper, ApplicationRobotRule::getRobotRuleId, robotRule.restore());
            }
            return copyRule;
        };

        // 遍历，固定只有两层级
        sourceRobotRules.forEach(robotRule -> {
            ApplicationRobotRule applicationRobotRule = copy.apply(robotRule, null);
            // 复制子级规则
            robotRule.getChildren().forEach(child -> copy.apply(child, applicationRobotRule.getRobotRuleId()));
        });

        // 删除列表
        List<ApplicationRobotRule> deletedList = targetRobotRules.stream()
                .filter(robotRule -> !ids.contains(robotRule.getRobotRuleId()))
                .peek(e -> e.setIsDeleted(SwitchEnum.NO.getId()))
                .collect(Collectors.toList());
        logService.saveAndCheck(this.baseMapper, ApplicationRobotRule::getRobotRuleId, deletedList);
    }

    /**
     * 转DTO 并且层级分类
     *
     * @param source source
     * @param rules  rules
     * @return List
     */
    private List<RobotRuleDTO> toDTO(RobotRuleSourceDTO source, List<ApplicationRobotRule> rules) {
        RuleForm sourceForm = this.getSourceForm(source);
        // 特殊控件base
        SpecialControlConfigDTO targetFormConfig = BeanUtil.copyProperties(sourceForm.targetForm.getSpecialControlConfig(), SpecialControlConfigDTO.class);
        Map<Long, ControlBaseInfoDTO> ruleTargetMap = OrderSpecialControlEnum.initControlBaseMap(targetFormConfig);
        SpecialControlConfigDTO sourceFormConfig = BeanUtil.copyProperties(sourceForm.sourceForm.getSpecialControlConfig(), SpecialControlConfigDTO.class);
        Map<Long, ControlBaseInfoDTO> ruleSourceMap = OrderSpecialControlEnum.initControlBaseMap(sourceFormConfig);
        // 表单控件base
        Set<Long> ruleTargetIds = new HashSet<>();
        Set<Long> ruleSourceId = new HashSet<>();
        rules.forEach(rule -> {
            if (ApplicationUtil.meaningful(rule.getRuleTargetId())) {
                ruleTargetIds.add(rule.getRuleTargetId());
            }
            if (ApplicationUtil.meaningful(rule.getRuleSourceId())) {
                ruleSourceId.add(rule.getRuleSourceId());
            }
        });
        ruleTargetMap.putAll(applicationFormControlService.findBaseInfoMapByControlIds(ruleTargetIds));
        ruleSourceMap.putAll(applicationFormControlService.findBaseInfoMapByControlIds(ruleSourceId));

        List<RobotRuleDTO> ruleList = new ArrayList<>();
        rules.forEach(rule -> {
            RobotRuleDTO robotRule = BeanUtil.copyProperties(rule, RobotRuleDTO.class);
            if (StringUtils.isNotBlank(rule.getDefaultValue())) {
                robotRule.setValues(JSON.parseArray(rule.getDefaultValue(), ControlDataValueDTO.class));
            }
            if (ApplicationUtil.meaningful(rule.getRuleTargetId())) {
                // 规则目标不存在，该规则无效
                if (!ruleTargetMap.containsKey(rule.getRuleTargetId())) {
                    return;
                }
                robotRule.setRuleTarget(ruleTargetMap.get(rule.getRuleTargetId()));
            }
            if (ApplicationUtil.meaningful(rule.getRuleSourceId())) {
                // 规则源不存在，该规则无效
                if (!ruleSourceMap.containsKey(rule.getRuleSourceId())) {
                    return;
                }
                robotRule.setRuleSource(ruleSourceMap.get(rule.getRuleSourceId()));
            }
            ruleList.add(robotRule);
        });
        return ApplicationUtil.classifyRobotRuleDTO(ruleList);
    }

    /**
     * 获取规则涉及表单 FIXME 待优化
     *
     * @param source source
     * @return RuleForm
     */
    private RuleForm getSourceForm(RobotRuleSourceDTO source) {
        ApplicationForm sourceForm;
        ApplicationForm targetForm;
        int release = ApplicationVersionEnum.RELEASE_VERSION.getId();
        switch (RobotRuleSourceTypeEnum.isExistById(source.getSourceType())) {
            case PROCESS_NODE_ROBOT:
                // 流程节点机器人规则
                ApplicationProcessNode processNode = applicationProcessNodeMapper.findById(source.getSourceId(), true);
                // 获取规则源表单
                ApplicationProcess process = applicationProcessMapper.findById(processNode.getProcessId());
                sourceForm = applicationFormMapper.findByAppIdAndVersion(process.getApplicationId(), process.getApplicationVersion());
                // 获取规则目标表单
                NodeTypeEnum nodeTypeEnum = NodeTypeEnum.isExistById(processNode.getNodeType());
                if (!nodeTypeEnum.isRobot()) {
                    throw new BusinessException(BaseEnum.RUN_ERROR);
                }
                RobotConfigDTO robotConfigDTO = JSON.parseObject(processNode.getRobotConfig(), RobotConfigDTO.class);
                targetForm = applicationFormMapper.findByAppIdAndVersion(robotConfigDTO.getTargetAppId(), release);
                break;
            case CUSTOM_BUTTON_RULE:
                // 自定义按钮赋值规则
                ApplicationButton button = applicationButtonMapper.findById(source.getSourceId());
                switch (ButtonSourceTypeEnum.isExistById(button.getSourceType())) {
                    case APPLICATION_BUTTON:
                        // 应用按钮
                        sourceForm = applicationFormMapper.findByAppIdAndVersion(button.getSourceId(), button.getVersion());
                        break;
                    case APPLICATION_VIEW_BUTTON:
                        // 应用视图按钮
                        ApplicationView view = applicationViewMapper.findById(button.getSourceId());
                        sourceForm = applicationFormMapper.findByAppIdAndVersion(view.getApplicationId(), button.getVersion());
                        break;
                    default:
                        throw ButtonSourceTypeEnum.getBusinessException();
                }
                // 获取规则源表单
                targetForm = applicationFormMapper.findByAppIdAndVersion(button.getTargetApplicationId(), release);
                break;
            case APPLICATION_ROBOT:
                // 应用机器人规则
                ApplicationRobot robot = applicationRobotMapper.findById(source.getSourceId());
                switch (RobotSourceTypeEnum.isExistById(robot.getSourceType())) {
                    case APPLICATION_ROBOT:
                        // 应用机器人
                        sourceForm = applicationFormMapper.findByAppIdAndVersion(robot.getSourceId(), robot.getApplicationVersion());
                        break;
                    case PROCESS_NODE_ROBOT:
                        // 流程节点机器人 (备注：业务上，不存在，流程机器人直接挂在在节点上，直接关联规则表)
                    default:
                        throw RobotRuleSourceTypeEnum.getBusinessException();
                }
                // 获取规则源表单
                targetForm = applicationFormMapper.findByAppIdAndVersion(robot.getTargetApplicationId(), release);
                break;
            default:
                throw RobotRuleSourceTypeEnum.getBusinessException();
        }
        return new RuleForm(sourceForm, targetForm);
    }

    /**
     * 复制
     *
     * @param source source
     * @param target target
     * @return ApplicationRobotRule
     */
    private ApplicationRobotRule copyProp(ApplicationRobotRule source, ApplicationRobotRule target) {
        if (null == target) {
            target = new ApplicationRobotRule();
        }
        target.setRobotRuleType(source.getRobotRuleType());
        target.setRuleTargetId(source.getRuleTargetId());
        target.setMatchingType(source.getMatchingType());
        target.setChangeType(source.getChangeType());
        target.setRuleSourceId(source.getRuleSourceId());
        target.setDefaultValue(source.getDefaultValue());
        target.setSourceRobotRuleId(source.getRobotRuleId());

        return target;
    }

    /**
     * 规则涉及表单
     */
    @AllArgsConstructor
    private static class RuleForm {
        /**
         * 规则源表单
         */
        private final ApplicationForm sourceForm;
        /**
         * 规则目标表单
         */
        private final ApplicationForm targetForm;
    }
}
